function [] = barplot(varargin);

% BARPLOT - creates bar plot with error bars
%
%    BARPLOT(X, Y, YERR) creates a bar plot with bar heights Y and
%    error bars YERR distributed along the ordinate X.  X must be
%    an M x 1 vector, whereas Y & YERR can be M x N arrays.  If Y
%    & YERR have multiple columns, then a group of N bars will be
%    plotted together at each value of X.
%
%    BARPLOT(H_FIG, 'open') initializes the global structure BAR_PLOT
%    and creates the bar plot display figure.
%
%    BARPLOT(H_FIG, 'import', TYPE) imports bar plot data from a
%    tab delimited ASCII text file (TYPE = 'txt'), from a MAT file
%    (TYPE = 'mat'), or from a workspace variable (TYPE = 'var').
%
%    BARPLOT(H_FIG, 'export', TYPE) saves the bar plot data as
%    tab delimited ASCII text file (TYPE = 'txt'), as a MAT file
%    a (TYPE = 'mat'), or as a workspace variable (TYPE = 'var').
%
%    BARPLOT(H_FIG, 'save', TYPE) saves the plot figure as a bitmap
%    file (TYPE = 'bitmap'), as a JPEG file (TYPE = 'jpeg'), as a TIFF
%    file (TYPE = 'tiff'), as a PostScript (TYPE = 'ps' or 'psc'), or
%    as an Encapsulated PostScript file (TYPE = 'eps' or 'epsc').
%
%    BARPLOT(H_FIG, 'dpi') sets the output resolution in dots per
%    inch.
%
%    BARPLOT(H_FIG, 'close') clears the global structure BAR_PLOT and
%    deletes the bar plot display figure.
%
%    BARPLOT(H_FIG, 'axes', TYPE) sets the scaling for the X (TYPE = 'X')
%    or Y (TYPE = 'Y') axes.
%
%    BARPLOT(H_FIG, 'labels') sets the axes labels.
%
%    BARPLOT(H_FIG, 'width', TYPE) sets the width for the column bars
%    (TYPE = 'column') or for the error bars (TYPE = 'error').
%
%    BARPLOT(H_FIG, 'visible', TYPE) sets the visible columns (TYPE =
%    'columns') or visible rows (TYPE = 'rows').
%
%    BARPLOT(H_FIG, 'legend', 'display') toggles the legend display.
%
%    BARPLOT(H_FIG, 'legend', 'pos') sets the legend position.
%
%    BARPLOT(H_FIG, 'legend', 'size') sets the legend size.
%
%    BARPLOT(H_FIG, 'legend', 'label') sets the legend labels.
%
%    BARPLOT(H_FIG, 'update') updates the display of bar plot data
%    data and error bars.

% By:   S.C. Molitor (smolitor@bme.jhu.edu)
% Date: May 31, 2000

% default parameters

max_dpi = 1200;		% maximum resolution (DPI)
max_width = 1;			% maximum width (relative to bar width)
max_pos = 1;			% maximum legend coordinate (relative to axis)
label_pos = [0.5 -0.1 0; -0.1 0.5 0];	% axes label positions relative to axes
label_rot = [0 90];	% axes label rotations

% global variables

global BAR_PLOT		% structure for passing data & parameters

% validate arguments

if ((nargin < 2) | (nargin > 3))
   msgbox('Invalid number of arguments', 'BARPLOT Error', 'warn');
   return
elseif (istype(varargin{1}, 'figure') & ischar(varargin{2}))
   h_fig = varargin{1};
   control_command = varargin{2};
   if (nargin == 2)
      control_type = '';
   elseif (ischar(varargin{3}) & ~isempty(varargin{3}))
      control_type = varargin{3};
   else
      msgbox('TYPE must be a character string', 'BARPLOT Error', 'warn');
      return
   end
elseif (isnumeric(varargin{1}) & isnumeric(varargin{2}))
   if (nargin ~= 3)
      msgbox('Syntax: BARPLOT(X, Y, YERR)', 'BARPLOT Error', 'warn');
      return
   elseif (isempty(varargin{1}) | (size(varargin{1}, 2) ~= 1))
      msgbox('X must be an M x 1 vector', 'BARPLOT Error', 'warn');
      return
   elseif (isempty(varargin{2}) | (size(varargin{1}, 1) ~= size(varargin{2}, 1)))
      msgbox('Y must be an M x N array', 'BARPLOT Error', 'warn');
      return
   elseif (~isnumeric(varargin{3}) | any(size(varargin{2}) ~= size(varargin{3})))
      msgbox('YERR must be an M x N array', 'BARPLOT Error', 'warn');
      return
   else
      x_data = varargin{1};
      y_data = varargin{2};
      y_err = varargin{3};
      control_command = 'open';
   end
else
   msgbox('Syntax: BARPLOT(H_FIG, COMMAND, TYPE)', 'BARPLOT Error', 'warn');
   return
end

% execute control command

switch (control_command)
   
case 'open'			% open bar plot display
   
   % initialize bar plot structure if needed
   
   field_name = {'x', 'y', 'err', 'col', 'row', 'axes', 'label', ...
         'width', 'dpi', 'display', 'pos', 'size', 'legend'};
   default_value = {[], [], [], [], [], [0 0; 0 0], {'X', 'Y'}, ...
         [1 0.75], 0, 'on', [0.7 0.7], [0.25 0.25], {}};
   BAR_PLOT = defstruc(BAR_PLOT, field_name, default_value);
   
   % assign data to structure
   
   BAR_PLOT.x = x_data;
   BAR_PLOT.y = y_data;
   BAR_PLOT.err = y_err;
   
   % specify all columns & rows as visible
   
   BAR_PLOT.col = 1 : size(BAR_PLOT.y, 2);
   BAR_PLOT.row = 1 : size(BAR_PLOT.y, 1);
   
   % add/delete legend labels if needed
   
   if (length(BAR_PLOT.legend) >= length(BAR_PLOT.col))
      BAR_PLOT.legend = {BAR_PLOT.legend{1 : length(BAR_PLOT.col)}};
   else
      for i = length(BAR_PLOT.legend) + 1 : length(BAR_PLOT.col)
         BAR_PLOT.legend{i} = sprintf('%d', i);
      end
   end
   
   % create bar plot display if needed
   
   h_fig = findobj(0, 'Type', 'figure', 'Tag', 'BARPLOT');
   if (length(h_fig) == 1)
      figure(h_fig);
   else
      h_fig = display_open('BARPLOT');
   end
   barplot(h_fig, 'update');
   
case 'import'		% import bar plot data
   read_data = readdata(control_type);
   if (size(read_data, 2) >= 3)
      barplot(read_data(:, 1), read_data(:, 2 : 2 : end), read_data(:, 3 : 2 : end));
   else
      msgbox('Imported array must have at least 3 columns', 'BARPLOT Error', 'warn');
   end
   
case 'export'		% export bar plot data
   save_data = zeros(size(BAR_PLOT.y, 1), 1 + 2*size(BAR_PLOT.y, 2));
   save_data(:, 1) = BAR_PLOT.x;
   save_data(:, 2 : 2 : end) = BAR_PLOT.y;
   save_data(:, 3 : 2 : end) = BAR_PLOT.err;
   savedata(save_data, control_type);
   
case 'save'			% export figure to graphics file
   
   % obtain plot resolution if specified
   % otherwise obtain file format string
   
   switch (control_type)
   case {'ps', 'psc'}
      file_ext = '*.ps';
   case {'eps', 'epsc'}
      file_ext = '*.eps';
   case 'bitmap'
      file_ext = '*.bmp';
   case 'jpeg'
      file_ext = '*.jpg';
   case 'tiff'
      file_ext = '*.tif';
   otherwise
      return
   end
   file_format = ['-d' control_type];
   
   % change to specified path if valid
   
   global DATA_PATH
   if (ischar(DATA_PATH))
      if (exist(DATA_PATH) == 7)
         cd(DATA_PATH);
      end
   end
   
   % get file name
   % export figure using specified format
   
   [file_name, temp_path] = uiputfile(file_ext, 'Export Figure to Graphics File');
   if ((file_name == 0) & (temp_path == 0))
      return
   else
      DATA_PATH = temp_path;
      print(h_fig, file_format, sprintf('-r%d', BAR_PLOT.dpi), [DATA_PATH file_name]); 
   end
   
case 'dpi'			% specify plot resolution
   title_string = 'Select output resolution (DPI)';
   [new_value, select_flag] = guivalue(title_string, 0, max_dpi, BAR_PLOT.dpi, 'int');
   if ((select_flag) & (new_value ~= BAR_PLOT.dpi))
      BAR_PLOT.dpi = new_value;
   end
   
case 'close'		% close bar plot display
   %clear global BAR_PLOT
   delete(h_fig);
   
case 'axes'			% set scaling on axes
   axes_index = find(strcmp({'X', 'Y'}, control_type));
   if (length(axes_index) ~= 1)
      return
   end
   [edit_array, edit_flag] = guiedit([control_type ' axes scale'], BAR_PLOT.axes(axes_index, :));
   if (edit_flag)
      BAR_PLOT.axes(axes_index, :) = edit_array;
      barplot(h_fig, 'update');
   end
   
case 'labels'		% edit axes labels
   [edit_cell, edit_flag] = guiedit('Edit axes labels', BAR_PLOT.label);
   if (edit_flag)
      BAR_PLOT.label = edit_cell;
      barplot(h_fig, 'update');
   end
   
case 'width'		% set column/error bar width
   width_index = find(strcmp({'column', 'error'}, control_type));
   if (length(width_index) ~= 1)
      return
   end
   title_string = ['Set ' control_type ' bar width'];
   [new_value, select_flag] = guivalue(title_string, 0, max_width, BAR_PLOT.width(width_index), 'real');
   if ((select_flag) & (new_value ~= BAR_PLOT.width(width_index)))
      BAR_PLOT.width(width_index) = new_value;
      barplot(h_fig, 'update');
   end
   
case 'visible'		% set visible columns/rows
   
   % obtain visible columns/rows
   % determine total number of columns/rows
   % create titles for check boxes
   
   switch (control_type)
   case 'columns'
      old_list = BAR_PLOT.col;
      num_radio = size(BAR_PLOT.y, 2);
      title_list = 1 : num_radio;
   case 'rows'
      old_list = BAR_PLOT.row;
      num_radio = size(BAR_PLOT.y, 1);
      title_list = BAR_PLOT.x';
   otherwise
      return
   end
   for i = 1 : length(title_list)
      radio_title{i} = sprintf('%d', title_list(i));
   end
   
   % obtain user-selected columns/rows
   % update display if selected
   
   figure_title = ['Select ' control_type ' to display'];
   [new_list, select_flag] = guiradio(figure_title, radio_title, old_list, 'multiple');
   if (select_flag & ~isempty(new_list))
      BAR_PLOT = setfield(BAR_PLOT, control_type(1 : 3), new_list);
      barplot(h_fig, 'update');
   end
   
case 'legend'		% edit legend labels
   
   switch (control_type)
      
   case 'display'		% toggle display of legend
      if (strcmp(BAR_PLOT.display, 'on'))
         BAR_PLOT.display = 'off';
      elseif (strcmp(BAR_PLOT.display, 'off'))
         BAR_PLOT.display = 'on';
      else
         return
      end
      h_menu = findobj(h_fig, 'Type', 'uimenu', 'Tag', 'legend');
      if (length(h_menu) == 1)
         guicheck(h_menu, BAR_PLOT.display, 'Tag', {'display'});
      end
      barplot(h_fig, 'update');
      
   case 'position'	% edit legend position
      [edit_array, edit_flag] = guiedit('Edit legend position (relative to axis)', BAR_PLOT.pos);
      if (edit_flag & any(edit_array ~= BAR_PLOT.pos))
         BAR_PLOT.pos = edit_array;
         barplot(h_fig, 'update');
      end
      
   case 'size'			% edit legend size
      [edit_array, edit_flag] = guiedit('Edit legend size (relative to axis)', BAR_PLOT.size);
      if (edit_flag & any(edit_array ~= BAR_PLOT.size))
         BAR_PLOT.size = edit_array;
         barplot(h_fig, 'update');
      end
      
   case 'labels'		% edit legend labels
      [edit_cell, edit_flag] = guiedit('Edit legend labels', BAR_PLOT.legend);
      if (edit_flag)
         BAR_PLOT.legend = edit_cell;
         barplot(h_fig, 'update');
      end
   end
   
case 'update'		% update bar plot display
   
   % obtain data to plot
   % close figure if no visible data
   % otherwise create bar plot
   
   x_data = BAR_PLOT.x(BAR_PLOT.row, 1);
   y_data = BAR_PLOT.y(BAR_PLOT.row, BAR_PLOT.col);
   y_err = BAR_PLOT.err(BAR_PLOT.row, BAR_PLOT.col);
   if (isempty(x_data) | isempty(y_data) | isempty(y_err))
      barplot(h_fig, 'close');
      return
   elseif (isscalar(x_data))
      x_data = [1 : size(y_data, 2)];
   end
   h_patch = bar(x_data, y_data, BAR_PLOT.width(1));
   
   % determine horizontal extent of error bars
   % account for relative width of error bars
   
   h_axes = get(h_patch(1), 'Parent');
   for i = 1 : length(h_patch)
      x_vertex = get(h_patch(i), 'XData');
      x_mean = mean(x_vertex, 1);
      x_min = BAR_PLOT.width(2)*min(x_vertex, [], 1) + (1 - BAR_PLOT.width(2))*x_mean;
      x_max = BAR_PLOT.width(2)*max(x_vertex, [], 1) + (1 - BAR_PLOT.width(2))*x_mean;
      
      % calculate vertical extent of error bars
      % account for negative & positive going bars
      
      y_vertex = get(h_patch(i), 'YData');
      [y_min, i_min] = max(abs(y_vertex), [], 1);
      y_max = y_min + y_err(:, i)';
      i_min = i_min + size(y_vertex, 1)*[0 : size(y_vertex, 2) - 1];
      y_min = y_min .* sign(y_vertex(i_min));
      y_max = y_max .* sign(y_vertex(i_min));
      
      % create horizontal & vertical error bar lines
      % only create error bars if error is positive
      
      for j = 1 : length(x_data)
         if (y_min(j) ~= y_max(j))
            h_line = line('Parent', h_axes, ...
               'XData', [x_mean(j) x_mean(j)], ...
               'YData', [y_min(j) y_max(j)], ...
               'Color', 'black');
            h_line = line('Parent', h_axes, ...
               'XData', [x_min(j) x_max(j)], ...
               'YData', [y_max(j) y_max(j)], ...
               'Color', 'black');
         end
      end
   end
   
   % set axes limits if specified
   
   if (diff(BAR_PLOT.axes(1, :)) > 0)
      set(h_axes, 'XLim', BAR_PLOT.axes(1, :));
   end
   if (diff(BAR_PLOT.axes(2, :)) > 0)
      set(h_axes, 'YLim', BAR_PLOT.axes(2, :));
   end
   
   % create axes labels
   
   for i = 1 : 2
      h_label(i) = text('Parent', h_axes, ...
         'Units', 'normalized', ...
         'Position', label_pos(i, :), ...
         'Color', [0 0 0], ...
         'FontName', 'Helvetica', ...
         'FontSize', 8, ...
         'HorizontalAlignment', 'center', ...
         'VerticalAlignment', 'middle', ...
         'Rotation', label_rot(i), ...
         'String', BAR_PLOT.label{i});
   end
   set(h_axes, 'XLabel', h_label(1), 'YLabel', h_label(2));
   
   % add legend if specified
   % obtain color map & indices used by bar plot
   
   if (strcmp(BAR_PLOT.display, 'on'))
      color_map = colormap;
      color_index = round(1 + (size(color_map, 1) - 1) * [0 : length(BAR_PLOT.col) - 1]/(length(BAR_PLOT.col) - 1));
      
      % determine legend layout
      
      x_lim = get(h_axes, 'XLim');
      y_lim = get(h_axes, 'YLim');
      rect_width = 4*BAR_PLOT.size(1)*diff(x_lim)/9;
      horiz_space = rect_width/4;
      rect_height = 2*BAR_PLOT.size(2)*diff(y_lim)/(3*length(BAR_PLOT.col) - 1);
      vert_space = rect_height/2;
      
      % create legend bars & labels
      
      x_offs = BAR_PLOT.pos(1)*diff(x_lim) + x_lim(1);
      y_offs = (BAR_PLOT.pos(2) + BAR_PLOT.size(2))*diff(y_lim) + y_lim(1) - rect_height;
      for i = 1 : min(length(h_patch), length(BAR_PLOT.col))
         h_rect(i) = rectangle('Parent', h_axes, ...
            'Position', [x_offs y_offs rect_width rect_height], ...
            'Curvature', [0 0], ...
            'EdgeColor', get(h_patch(i), 'EdgeColor'), ...
            'FaceColor', color_map(color_index(i), :));
         h_text(i) = text('Parent', h_axes, ...
            'Units', 'data', ...
            'FontName', 'Helvetica', ...
            'FontSize', 8, ...
            'HorizontalAlignment', 'left', ...
            'VerticalAlignment', 'middle', ...
            'Position', [(x_offs + rect_width + horiz_space) (y_offs + rect_height/2)], ...
            'String', BAR_PLOT.legend{BAR_PLOT.col(i)});
         y_offs = y_offs - rect_height - vert_space;
      end
   end
end
return



function h_fig = display_open(fig_tag);

% DISPLAY_OPEN - create window for bar plot display
%
%    H_FIG = DISPLAY_OPEN(TAG) creates a figure for the display
%    of bar plot data.  The figure has a tag value TAG.

% By:   S.C. Molitor (smolitor@med.unc.edu)
% Date: May 3, 2000

% default parameters

figure_bkgnd = [1 1 1];					% figure window background colorspec
figure_pos = [0.25 0.25 0.5 0.5];	% figure window position (% of screen)
figure_title = 'Bar Plot';				% figure window title

% check input arguments

if (nargin ~= 1)
   return
elseif (isempty(fig_tag) | ~ischar(fig_tag))
   return
end

% global variables

global BAR_PLOT		% structure for passing data & parameters

% create bar plot figure

h_fig = figure('Color', figure_bkgnd, ...
   'Units', 'normalized', ...
   'Position', figure_pos, ...
   'BackingStore', 'on', ...
   'DeleteFcn', 'barplot(gcbo, ''close'')', ...
   'MenuBar', 'none', ...
   'Name', figure_title, ...
   'NumberTitle', 'off', ...
   'Pointer', 'arrow', ...
   'Tag', fig_tag, ...
   'UserData', []);

% create drawing display axes

h_axes = axes('Parent', h_fig, ...
   'Units', 'normalized', ...
   'Position', [0.1 0.1 0.8 0.8], ...
   'FontName', 'Helvetica', ...
   'FontSize', 8, ...
   'XDir', 'normal', ...
   'XLimMode', 'auto', ...
   'XTickMode', 'auto', ...
   'XTickLabelMode', 'auto', ...
   'YDir', 'normal', ...
   'YLimMode', 'auto', ...
   'YTickMode', 'auto', ...
   'YTickLabelMode', 'auto');

% create File menu

h_menu(1) = uimenu(...
   'Parent', h_fig, ...
   'Label', '&File', ...
   'Accelerator', 'f', ...
   'Tag', 'file');
menu_label = {'Import Data', 'Export Data', 'Export PS', 'Export Image', 'Resolution ...', 'Close'};
call_back = {'',  '', '',  '', 'barplot(gcf, ''dpi'')', 'delete(gcf)'};
menu_sep = {'off', 'off', 'on', 'off', 'on', 'on'};
h_item = guimenu(h_menu(1), 'Label', menu_label, 'Callback', call_back, 'Separator', menu_sep);

% create import data submenu

menu_label = {'Text File...', 'MAT File...', 'Workspace Array...'};
call_back = {'barplot(gcf, ''import'', ''txt'')', 'barplot(gcf, ''import'', ''mat'')', ...
      'barplot(gcf, ''import'', ''var'')'};
h_sub = guimenu(h_item(1), 'Label', menu_label, 'Callback', call_back);

% create export data submenu

menu_label = {'Text File...', 'MAT File...', 'Workspace Array...'};
call_back = {'barplot(gcf, ''export'', ''txt'')', 'barplot(gcf, ''export'', ''mat'')', ...
      'barplot(gcf, ''export'', ''var'')'};
h_sub = guimenu(h_item(2), 'Label', menu_label, 'Callback', call_back);

% create export PS submenu

menu_label = {'B/W PS File ...', 'B/W EPS File ...', 'Color PS File ...', 'Color EPS File ...'};
call_back = {'barplot(gcf, ''save'', ''ps'')',  'barplot(gcf, ''save'', ''eps'')', ...
      'barplot(gcf, ''save'', ''psc'')',  'barplot(gcf, ''save'', ''epsc'')'};
h_sub = guimenu(h_item(3), 'Label', menu_label, 'Callback', call_back);

% create export image submenu

menu_label = {'Bitmap Image ...', 'JPEG Image ...', 'TIFF Image ...'};
call_back = {'barplot(gcf, ''save'', ''bitmap'')',  'barplot(gcf, ''save'', ''jpeg'')', ...
      'barplot(gcf, ''save'', ''tiff'')'};
h_sub = guimenu(h_item(4), 'Label', menu_label, 'Callback', call_back);

% create layout menu

h_menu(2) = uimenu(...
   'Parent', h_fig, ...
   'Label', '&Layout', ...
   'Accelerator', 'l', ...
   'Tag', 'layout');
menu_label = {'Column Bar Width ...', 'Error Bar Width ...', 'Visible Columns ...', 'Visible Rows ...'};
call_back = {'barplot(gcf, ''width'', ''column'')', 'barplot(gcf, ''width'', ''error'')', ...
      'barplot(gcf, ''visible'', ''columns'')', 'barplot(gcf, ''visible'', ''rows'')'};
menu_sep = {'off', 'off', 'on', 'off'};
h_item = guimenu(h_menu(2), 'Label', menu_label, 'Callback', call_back, 'Separator', menu_sep);

% create axes menu

h_menu(3) = uimenu(...
   'Parent', h_fig, ...
   'Label', '&Axes', ...
   'Accelerator', 'a', ...
   'Tag', 'axes');
menu_label = {'X scale ...', 'Y scale ...', 'Labels ...'};
call_back = {'barplot(gcf, ''axes'', ''X'')',  'barplot(gcf, ''axes'', ''Y'')', 'barplot(gcf, ''labels'')'};
menu_sep = {'off', 'off', 'on'};
h_item = guimenu(h_menu(3), 'Label', menu_label, 'Callback', call_back, 'Separator', menu_sep);

% create legend menu
% place check mark next to legend display

h_menu(4) = uimenu(...
   'Parent', h_fig, ...
   'Label', 'Le&gend', ...
   'Accelerator', 'g', ...
   'Tag', 'legend');
menu_label = {'Display Legend', 'Position ...', 'Size ...', 'Labels ...'};
call_back = {'barplot(gcf, ''legend'', ''display'')', 'barplot(gcf, ''legend'', ''position'')', ...
      'barplot(gcf, ''legend'', ''size'')', 'barplot(gcf, ''legend'', ''labels'')'};
menu_sep = {'off', 'on', 'off', 'on'};
menu_tag = {'display', 'size', 'position', 'labels'};
h_item = guimenu(h_menu(4), 'Label', menu_label, 'Callback', call_back, 'Separator', menu_sep, 'Tag', menu_tag);
guicheck(h_menu(4), BAR_PLOT.display, 'Tag', {'display'});
return
